<template>
  <div class="formula-edit">
    <div class="formula-edit-wrap">
      <textarea ref="textarea"/>
      <div v-if="formulaError" class="error-tip">公式错误</div>
    </div>
    <div class="tool-list">
      <div class="tool-item" style="margin-right: 12px;">
        <div class="label">引用字段</div>
        <FieldTree :field-list="fieldList" is-field @selectField="addField" />
      </div>
      <div class="tool-item">
        <div class="label">引用函数</div>
        <FieldTree :field-list="functionList" placeholder="搜索函数" @selectField="addFunction" />
      </div>
    </div>
  </div>
</template>

<script>
// components
import FieldTree from './fieldTree';
// CodeMirror
import './formula-mode';
import './formula-hint';
import CodeMirror from 'codemirror';
import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/hint/show-hint.css';
import './show-hint';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/edit/closebrackets';
// tool
import { functionList } from './functions';
import { mathFormulaFunction } from '@/utils/formulaMath';
// import { isNumberType } from '@/utils/formMakeUtils';

export default {
  components: { FieldTree },
  props: {
    fieldList: {
      type: Array,
      default: () => ([])
    },
    formOptions: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      formulaString: '',
      functionList,
      options: {
        autoCloseBrackets: false,
        matchbrackets: true,
        tabSize: 4, // 缩进格式
        theme: 'default', // 指定主题，js 需要提前引入
        lineNumbers: false, // 是否显示行号
        // 指定语言类型,如果需要编辑和显示其他语言,需要import语言js然后修改此配置
        line: true,
        styleActiveLine: true, // 高亮选中行
        lineWrapping: true, // 是否自动换行
        // 是否为只读,如果为"nocursor" 不仅仅为只读 连光标都无法在区域聚焦
        readOnly: false,
        mode: 'text/fx-formula',
        hintOptions: {
          completeSingle: false, // 当匹配只有一项的时候是否自动补全
          pickCallback: this.pickCallback
        }
      },
      cminstance: null,
      formulaError: false
    };
  },
  mounted() {
    this.initialize();
  },
  methods: {
    // 回填数据
    setDefault(formulaString) {
      this.cminstance.setValue(formulaString);
      this.freshField();
      if (formulaString) {
        this.$nextTick(() => {
          const tempArr = formulaString.split('\n');
          const lineNum = tempArr && tempArr.length ? tempArr.length - 1 : 0;
          const pos = {
            line: lineNum, // 行号
            ch: formulaString.length // 光标位置
          };
          this.cminstance.setCursor(pos);
        });
      }
    },
    addField(field) {
      if (!field || !field.key) return;
      const fieldKey = `$_widget_${field.key}_end$`;
      this.addValue(fieldKey);
      const cminstance = this.cminstance;
      const curpos = cminstance.getCursor();
      const pos1 = {
        line: curpos.line, // 行号
        ch: curpos.ch - fieldKey.length + 1// 光标位置
      };
      const pos2 = {
        line: curpos.line, // 行号
        ch: curpos.ch + 1// 光标位置
      };
      this.appendField(field.name, pos1, pos2);
    },
    addFunction(data) {
      this.addValue(data.name + '()');
    },
    addValue(str) {
      const cminstance = this.cminstance;
      const post1 = cminstance.getCursor();
      const pos2 = {
        line: post1.line, // 行号
        ch: post1.ch // 光标位置
      };
      cminstance.replaceRange(str, pos2); // 替换内容
      pos2.ch = pos2.ch + (str.length - 1);
      cminstance.setCursor(pos2);
      cminstance.focus();
    },
    initialize() {
      this.formulaError = false;
      const mathFunction = _.debounce(this.mathFunction, 800);
      this.cminstance = CodeMirror.fromTextArea(this.$refs.textarea, this.options);
      this.cminstance.on('change', cm => {
        this.formulaString = cm.getValue();
        if (this.formulaString) {
          mathFunction(this.formulaString);
        } else {
          this.formulaError = false;
        }
      });
      this.cminstance.on('keypress', () => {
        // 触发自动补全提示
        this.cminstance.showHint();
      });
      this.cminstance.on('paste', (e) => {
        this.freshField();
      });
      this.refresh();
    },
    refresh() {
      this.$nextTick(() => {
        this.cminstance.refresh();
      });
    },
    destroy() {
      // garbage cleanup
      const element = this.cminstance.doc.cm.getWrapperElement();
      element && element.remove && element.remove();
    },
    pickCallback(data) {
      this.addValue('()');
    },
    appendField(fieldName, pos1, pos2) {
      const cminstance = this.cminstance;
      const domSpan = document.createElement('span');
      domSpan.innerHTML = fieldName;
      domSpan.setAttribute('class', 'cm-field');
      cminstance.markText(pos1, pos2, {
        atomic: true,
        handleMouseEvents: true,
        replacedWith: domSpan
      });
      cminstance.setCursor(pos2);
      cminstance.focus();
    },
    // 把总公式里面的字段转化成标识
    freshField() {
      this.$nextTick(() => {
        const cminstance = this.cminstance;
        cminstance.eachLine((line) => {
          const lineNum = cminstance.getLineNumber(line);
          const fieldList = this.fieldList;
          const formulaString = line.text;
          const widget = /\$_widget_(.+?)_end\$/g;
          let widgetReg;
          if (formulaString && formulaString.indexOf('$_widget') >= 0) {
            while ((widgetReg = widget.exec(formulaString)) !== null) {
              const itemFieldKey = widgetReg[1];
              const itemField = fieldList.find(i => i.key === itemFieldKey);
              if (itemField) {
                const pos1 = { line: lineNum, ch: widgetReg['index'] };
                const pos2 = { line: lineNum, ch: widgetReg['index'] + widgetReg[0].length };
                this.appendField(itemField.name, pos1, pos2);
              }
            }
          }
        });
      });
    },
    mathFunction(formulaString) {
      const fieldList = _.cloneDeep(this.fieldList);
      try {
        const scoped = {};
        fieldList.forEach(element => {
          const match = `$_widget_${element.key}_end$`;
          const mockValue = 1;
          // const mockValue = isNumberType(element) || element.type === 'date' ? 1 : '1';
          if (element.isSubTable) {
            scoped[match] = [mockValue];
          } else {
            scoped[match] = mockValue;
          }
        });
        const data = mathFormulaFunction(formulaString, scoped);

        if (data instanceof Error && data.toString() !== 'Error: #VALUE!') {
          throw data;
        } else {
          this.formulaError = false;
        }
      } catch (error) {
        console.log(error);
        this.formulaError = true;
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.formula-edit {
  font-size: 12px;
  font-family: PingFangSC-Regular;
  .formula-edit-wrap {
    border: 1px solid #e0e0e0;
    padding-bottom: 28px;
    position: relative;
    .error-tip {
      position: absolute;
      width: 100%;
      line-height: 32px;
      padding: 0 10px;
      font-size: 12px;
      background: #FFF1F1;
      color: #7D241C;
    }
    &::v-deep {
      .CodeMirror {
        height: auto;
        min-height: 60px;
        max-height: 300px;
        .CodeMirror-line {
            line-height: 20px;
        }
        .CodeMirror-widget {
          .cm-field {
            color: #406eff;
            background: rgba(64,110,255,.1);
            padding: 0px 5px;
            margin: 1px;
            display: inline-block;
          }
        }
      }
    }
  }
}
.tool-list {
  margin-top: 12px;
  display: flex;
  .width220 {
    width: 220px;
  }
  .label {
    font-size: 12px;
    color: #91A1B7;
    line-height: 18px;
    margin-bottom: 5px;
  }
}
</style>
<style lang="scss">
.CodeMirror-hints {
  z-index: 3000;
  .CodeMirror-hint {
    font-size: 14px;
  }
}
</style>
